/*
 * block_queue.h
 *
 *  Created on: 2010/07/25
 *      Author: terry
 */

#ifndef BLOCK_QUEUE_H_
#define BLOCK_QUEUE_H_

#include <string>
#include <queue>
#include <boost/smart_ptr.hpp>
#include <boost/thread.hpp>

using namespace std;
using namespace boost;

// blocking queue
template <typename T>
class block_queue
{

public:
    // default bound
    static const int DEFAULT_BOUND = 1000;
    block_queue();
    explicit block_queue(int _bound) ;
    virtual ~block_queue();
    virtual void dispose();
    shared_ptr<T> get();
    shared_ptr<T> get(long timeout);
    int put(shared_ptr<T> msg);
    int put(shared_ptr<T> msg, long timeout);
    virtual int get_bound();
    virtual int get_size();
private:
    int bound;
    bool disposed;
    queue< shared_ptr<T> > elements;
    mutex queue_guard;
    condition_variable get_state;
    condition_variable put_state;
};

template <typename T>
block_queue<T>::block_queue()
    : bound(DEFAULT_BOUND), disposed(false)
{
}

template <typename T>
block_queue<T>::block_queue(int _bound) 
    : bound(_bound), disposed(false)
{
}

template <typename T>
block_queue<T>::~block_queue() 
{
    if(!this->disposed){
        this->dispose();
    }
}

template <typename T>
void block_queue<T>::dispose() 
{
    this->disposed = true;
    this->put_state.notify_all();
    this->get_state.notify_all();
}

template <typename T>
shared_ptr<T> block_queue<T>::get()
{
    return this->get(-1);
}

template <typename T>
shared_ptr<T> block_queue<T>::get(long timeout)
{
    // まず、キューをロックする。
    unique_lock<mutex> lck(this->queue_guard);
    if (get_size() <= 0) {
        // キューに要素なし場合、
        // 指定されたタイムアウト時間より、分けて処理する。
        if(timeout == 0){
            // タイムアウトが0の場合、待たないで失敗コードを返す。
            return shared_ptr<T>();
        } else if(timeout < 0){
            // タイムアウトが0より小さい場合、ずっと待つ。
            this->get_state.wait(lck);
        } else if(timeout > 0){
            // タイムアウトが0より大きい場合、指定された時間で待つ。
            system_time end_time = get_system_time() + posix_time::milliseconds(timeout);
            if(!this->get_state.timed_wait(lck, end_time)){
                return shared_ptr<T>();
            }
        }
    }


    if(disposed){
        return shared_ptr<T>();
    }

    // キューに要素あり、要素を取る
    shared_ptr<T> el = elements.front();
    elements.pop();

    // 入れる状態を知らせる。
    this->put_state.notify_one();
    return el;
}


template <typename T>
int block_queue<T>::put(shared_ptr<T> msg)
{
    return this->put(msg, -1);
}

template <typename T>
int block_queue<T>::put(shared_ptr<T> msg, long timeout)
{
    // まず、キューをロックする。
    unique_lock<mutex> lck(this->queue_guard);
    if (get_size() >= bound) {
        // キューに要素個数が最大値により等しい場合、
        // 指定されたタイムアウト時間より、処理する。
        if(timeout == 0){
            // タイムアウトが0の場合、待たないで失敗コードを返す。
            return -1;
        } else if(timeout < 0){
            // タイムアウトが0より小さい場合、ずっと待つ。
            this->put_state.wait(lck);
        } else if(timeout > 0){
            // タイムアウトが0より大きい場合、指定された時間で待つ。
            system_time end_time = get_system_time() + posix_time::milliseconds(timeout);
            if(!this->put_state.timed_wait(lck, end_time)){
                return -1;
            }
        }
    } 

    if(disposed){
        return -1;
    }

    // キューに空間あり、要素を入れる
    this->elements.push(msg);
    // 取得状態を知らせる。
    this->get_state.notify_one();

    return this->get_size();
}


template <typename T>
int block_queue<T>::get_bound()
{
    return this->bound;
}


template <typename T>
int block_queue<T>::get_size()
{
    return this->elements.size();
}

#endif /* BLOCK_QUEUE_H_ */

